package com.ruoyi.web.controller.vivo;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.RedisCache;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.dto.AdList;
import com.ruoyi.system.service.IVivoTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * VivoApiController -
 *
 * @author Jack
 * @date 2025/7/29
 * Description:
 */

@RestController
@RequestMapping("/vivo/api")
public class VivoApiController extends BaseController {

    @Autowired
    private IVivoTokenService vivoTokenService;

    @Autowired
    private VivoApiUtil vivoApiUtil;

    @Autowired
    private RedisCache redisCache;

    /***
    *  获取资质ID缓存前缀，方便管理和查找
    * */
    private static final String VIVO_QUALIFICATION_CACHE_PREFIX = "vivo:zizhiid:";


    private static final String VIVO_ADPLAN_CACHE_PREFIX = "vivo:adPlan:";
    private static final String VIVO_TOKEN_CACHE_PREFIX = "vivo:token:";
    private static final String VIVO_ADGROUP_CACHE_PREFIX = "vivo:adGroup:";
    /***
     *   资质ID缓存有效期，30分钟
     * */
    private static final long CACHE_ZIZHIID_EXPIRATION_TIME_MINUTES = 30;

    /***
     *  根据导入数据补齐相关字段返回
     * */
    @PostMapping("/importData")
    public AjaxResult importData(@RequestBody MultipartFile file) throws Exception {
        ExcelUtil<AdList> util = new ExcelUtil<>(AdList.class);
        List<AdList> adList = util.importExcel(file.getInputStream());


        // 循环处理每一条广告信息
        for (AdList adInfo : adList) {

            adInfo.setSuuid(UUID.randomUUID().toString().replace("-", ""));

            String mainName = adInfo.getMainName();
            String subName = adInfo.getSubName(); // 注意：你的DTO中应有 getSbuName() 方法
            String category = adInfo.getCategory(); // 获取资质类别

            // 1. 获取二代Token
            String token = redisCache.getCacheObject(VIVO_TOKEN_CACHE_PREFIX+mainName);
            if (token == null || token.isEmpty()) {
                // 本地缓存未命中，查询数据库
                token = vivoTokenService.selectVivoTokenByName(mainName);
                if (token == null || token.isEmpty()) {
                    return error("二代账户【" + mainName + "】无相关Token，联系管理员添加");
                }
                redisCache.setCacheObject(VIVO_TOKEN_CACHE_PREFIX+mainName, token, 2, TimeUnit.DAYS);
            }
            // token 已经获取到，无论是从缓存还是数据库

            // 2. 构建资质ID的缓存Key：使用 mainName, subName 和 category 组合作为唯一标识
            String qualificationCacheKey = VIVO_QUALIFICATION_CACHE_PREFIX + mainName + ":" + subName + ":" + category;

            // 3. 尝试从Redis缓存中获取资质ID
            String cachedAdvertiseQualificationId = redisCache.getCacheObject(qualificationCacheKey);

            if (cachedAdvertiseQualificationId != null && !cachedAdvertiseQualificationId.isEmpty()) {
                // 缓存命中：直接使用缓存中的资质ID，并设置Token
                adInfo.setAdvertiseQualificationId(cachedAdvertiseQualificationId);
//                adInfo.setToken(token); // 确保 token 也在 adInfo 中
                redisCache.expire(qualificationCacheKey,CACHE_ZIZHIID_EXPIRATION_TIME_MINUTES);
                // System.out.println("从Redis缓存中获取资质ID: " + cachedAdvertiseQualificationId + " for key: " + qualificationCacheKey); // 调试信息
                continue; // 跳过当前循环，处理下一个 AdList
            }

            // 4. Redis缓存未命中：调用Vivo API请求资质列表
            // 注意：这里你的 vivoApiUtil.post() 方法签名中，第二个参数是 adInfo.getUuid()，如果这个 UUID 每次都不同，
            // 那么理论上每次请求的 URL 都是不同的，但因为 Vivo 的 qualifycation/list 接口通常是根据账户和资质类型查询，
            // 所以即使 UUID 每次不同，只要 mainName, subName, category 相同，结果也应该一致。
            // 实际调用时，请确认 Vivo API 的设计，UUID 是否真的影响资质查询结果。
            JSONObject request = JSONObject.parseObject(vivoApiUtil.post("/v1/advertise/qualification/list", adInfo.getUuid(), token, "{}"));

            if (request.getInteger("code") != 0) {
                // Vivo API返回错误
                return error("二代账户【" + mainName + "】,子账户【" + subName + "】无相关资质，请检查资质名称是否正确. Vivo API 返回错误码: " + request.getInteger("code") + ", 错误信息: " + request.getString("message"));
            }

            // 5. 从Vivo API响应中提取资质ID
            String advertiseQualificationId = vivoApiUtil.getAdvertiseQualificationId(request, adInfo.getCategory());

            // 检查 advertiseQualificationId 是否有效
            if (advertiseQualificationId == null || advertiseQualificationId.isEmpty()) {
                return error("二代账户【" + mainName + "】,子账户【" + subName + "】的资质【" + category + "】在Vivo API响应中未找到有效的资质ID。请检查VivoApiUtil.getAdvertiseQualificationId方法或API返回。");
            }

            adInfo.setAdvertiseQualificationId(advertiseQualificationId);
            adInfo.setToken(token); // 确保 token 也在 adInfo 中

            // 6. 将获取到的资质ID存入Redis缓存，并设置有效期
            redisCache.setCacheObject(qualificationCacheKey, advertiseQualificationId, (int) CACHE_ZIZHIID_EXPIRATION_TIME_MINUTES, TimeUnit.MINUTES);
        }
        return success(adList);
    }

    @PostMapping("/addPlan")
    public AjaxResult addPlan (@RequestBody JSONObject body) {
        try {
            JSONObject request = JSONObject.parseObject(vivoApiUtil.post("/v1/ad/campaign/add", body.getString("uuid"), getTokenByName(body.getString("token")),body.getString("jsonStr")));
            return success(request);
        }catch (Exception e){
            return error(e.getMessage());
        }
    }

    @PostMapping("/addGroup")
    public AjaxResult addGroup (@RequestBody JSONObject body) {
        try {
            JSONObject request = JSONObject.parseObject(vivoApiUtil.post("/v1/ad/group/add", body.getString("uuid"), getTokenByName(body.getString("token")),body.getString("jsonStr")));
            return success(request);
        }catch (Exception e){
            return error(e.getMessage());
        }
    }

    @PostMapping("/addAd")
    public AjaxResult addAd (@RequestBody JSONObject body) {
        try {
            JSONObject request = JSONObject.parseObject(vivoApiUtil.post("/v1/ad/advertisement/add/customize/adCreative", body.getString("uuid"), getTokenByName(body.getString("token")),body.getString("jsonStr")));
            return success(request);
        }catch (Exception e){
            return error(e.getMessage());
        }
    }


    /***
     * 查询广告组可用转化目标
     * */
    @PostMapping("/getCvType")
    public String getCvType(@RequestBody JSONObject body ) throws IOException {
        return vivoApiUtil.post("/v1/ad/group/cvType/get", body.getString("uuid"), getTokenByName(body.getString("token")),body.getString("jsonStr"));
    }

    /***
     * 查询广告计划id
     * */
    @PostMapping("/getPlanId")
    public AjaxResult getPlanId(@RequestBody JSONObject body )  {
        try {
            String adPlanCacheKey = VIVO_ADPLAN_CACHE_PREFIX +  body.getString("uuid") + ":" + getTokenByName(body.getString("token")) + ":" +  body.getJSONObject("jsonStr").getString("name");
            Long cachedAdPlanId = redisCache.getCacheObject(adPlanCacheKey);
            if (cachedAdPlanId != null && cachedAdPlanId>0L) {
                redisCache.expire(adPlanCacheKey,CACHE_ZIZHIID_EXPIRATION_TIME_MINUTES);
               return success(cachedAdPlanId);
            }
            JSONObject request = JSONObject.parseObject(vivoApiUtil.post("/v1/ad/campaign/pageInfo", body.getString("uuid"), getTokenByName(body.getString("token")),body.getString("jsonStr")));
            if (request.getInteger("code") != 0) {
                return error(request.getString("message"));
            }
            Long adPlanId = vivoApiUtil.findIdByName(request.getJSONObject("data").getJSONArray("list"), body.getJSONObject("jsonStr").getString("name"));
            if (adPlanId != null &&  adPlanId>0L) {
                redisCache.setCacheObject(adPlanCacheKey, adPlanId, (int) CACHE_ZIZHIID_EXPIRATION_TIME_MINUTES, TimeUnit.MINUTES);
            }
            return success(adPlanId);
        }catch (Exception e){
            return error(e.getMessage());
        }
    }

    /***
     * 查询广告计划id
     * */
    @PostMapping("/getGroupId")
    public AjaxResult getGroupId(@RequestBody JSONObject body )  {
        try {
            String adGroupCacheKey = VIVO_ADGROUP_CACHE_PREFIX +  body.getString("uuid") + ":" + getTokenByName(body.getString("token")) + ":" +  body.getJSONObject("jsonStr").getString("name");
            Long cachedAdGroupId = redisCache.getCacheObject(adGroupCacheKey);
            if (cachedAdGroupId != null && cachedAdGroupId>0L) {
                redisCache.expire(adGroupCacheKey,CACHE_ZIZHIID_EXPIRATION_TIME_MINUTES);
                return success(cachedAdGroupId);
            }
            JSONObject request = JSONObject.parseObject(vivoApiUtil.post("/v1/ad/group/pageInfo", body.getString("uuid"), getTokenByName(body.getString("token")),body.getString("jsonStr")));
            if (request.getInteger("code") != 0) {
                return error(request.getString("message"));
            }
            Long adPlanId = vivoApiUtil.findIdByName(request.getJSONObject("data").getJSONArray("list"), body.getJSONObject("jsonStr").getString("name"));
            if (adPlanId != null &&  adPlanId>0L) {
                redisCache.setCacheObject(adGroupCacheKey, adPlanId, (int) CACHE_ZIZHIID_EXPIRATION_TIME_MINUTES, TimeUnit.MINUTES);
            }
            return success(adPlanId);
        }catch (Exception e){
            return error(e.getMessage());
        }
    }

    /***
     * 查询创意类型
     * */
    @PostMapping("/creativeType")
    @ResponseBody
    public String creativeType(@RequestBody JSONObject body ) throws IOException {
        Object cacheObject = redisCache.getCacheObject("vivo:longtime:creativeType:"+body.getString("jsonStr"));
        if(cacheObject!=null){
            return  cacheObject.toString();
        }

        String bak = vivoApiUtil.post("/v1/ad/positionList/creativeType", body.getString("uuid"), getTokenByName(body.getString("token")),body.getString("jsonStr"));
        JSONObject jsonObject = JSONObject.parseObject(bak);
        Long code = jsonObject.getLong("code");
        if(code==0){
            redisCache.setCacheObject("vivo:longtime:creativeType:"+body.getString("jsonStr"),bak);
        }
        return bak;
    }
    /***
     * 查询创意规格
     * */
    @PostMapping("/normElementList")
    @ResponseBody
    public String normElementList(@RequestBody JSONObject body ) throws IOException {
        Object cacheObject = redisCache.getCacheObject("vivo:longtime:normList:"+body.getString("jsonStr"));
        if(cacheObject!=null){
            return  cacheObject.toString();
        }
        String bak = vivoApiUtil.post("/v1/ad/material/norm/element/list", body.getString("uuid"), getTokenByName(body.getString("token")),body.getString("jsonStr"));
        JSONObject jsonObject = JSONObject.parseObject(bak);
        Long code = jsonObject.getLong("code");
        if(code==0){
            redisCache.setCacheObject("vivo:longtime:normList:"+body.getString("jsonStr"),bak);
        }
        return bak;
    }
    /**
     * 查询投放位置列表
     * */
    @PostMapping("/virtualPositionList")
    @ResponseBody
    public String virtualPositionList(@RequestBody JSONObject body ) throws IOException {
        Object cacheObject = redisCache.getCacheObject("vivo:longtime:virtualPositionList:"+body.getString("jsonStr"));
        if(cacheObject!=null){
            return  cacheObject.toString();
        }
        String bak =vivoApiUtil.post("/v1/ad/positionList/virtualPositionList", body.getString("uuid"), getTokenByName(body.getString("token")),body.getString("jsonStr"));
        JSONObject jsonObject = JSONObject.parseObject(bak);
        Long code = jsonObject.getLong("code");
        if(code==0){
            redisCache.setCacheObject("vivo:longtime:virtualPositionList:"+body.getString("jsonStr"),bak);
        }
        return bak;
    }
    /**
     * 上传文件获取平台文件code
     */
    @PostMapping("/setFile")
    public AjaxResult setFile(
            @RequestPart(value = "file") MultipartFile multipartFile,
            @RequestPart(value = "body") JSONObject body) {
        File tempFile = null;
        try {
            // 1. 处理可选的身体参数
            if (body != null) {
                // 这里可以解析 body 中的其他参数
                System.out.println("接收到的body参数: " + body.toJSONString());
            }

            // 2. 验证文件
            if (multipartFile.isEmpty()) {
                return error("文件不能为空");
            }

            // 3. 创建临时文件
            String originalFilename = multipartFile.getOriginalFilename();
            String tempDir = System.getProperty("java.io.tmpdir");
            String tempFileName = "upload_" + UUID.randomUUID() + "_" + (originalFilename != null ? originalFilename : "file");
            tempFile = new File(tempDir, tempFileName);

            // 4. 将 MultipartFile 转换为临时文件
            Files.copy(multipartFile.getInputStream(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);

            // 5. 计算文件的 MD5（使用try-with-resources确保流关闭）
            String md5 = calculateFileMD5(tempFile);

            // 6. 构建返回结果
            JSONObject result = new JSONObject();
            result.put("fileName", originalFilename);
            result.put("fileSize", multipartFile.getSize());
            result.put("fileType", multipartFile.getContentType());
            result.put("md5", md5);
            result.put("uploadTime", System.currentTimeMillis());

            if (body != null) {
                result.put("bodyParams", body);
            }

            return success(result);

        } catch (Exception e) {
            return error("文件处理失败: " + e.getMessage());
        } finally {
            // 7. 删除临时文件
            deleteTempFile(tempFile);
        }
    }

    /**
     * 计算文件的 MD5 值（使用 try-with-resources 自动关闭流）
     */
    private String calculateFileMD5(File file) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        byte[] buffer = new byte[8192];
        int read;

        // 使用 try-with-resources 自动关闭流
        try (FileInputStream fis = new FileInputStream(file)) {
            while ((read = fis.read(buffer)) != -1) {
                digest.update(buffer, 0, read);
            }
        }

        byte[] md5Bytes = digest.digest();
        StringBuilder hexString = new StringBuilder();
        for (byte b : md5Bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    /**
     * 安全删除临时文件
     */
    private void deleteTempFile(File tempFile) {
        if (tempFile != null && tempFile.exists()) {
            try {
                if (tempFile.delete()) {
                    System.out.println("临时文件已删除: " + tempFile.getAbsolutePath());
                } else {
                    logger.debug("删除临时文件失败: {}", tempFile.getAbsolutePath());
                    //尝试在JVM退出时删除
                    tempFile.deleteOnExit();
                }
            } catch (SecurityException e) {
                logger.debug("没有权限删除临时文件: {}", tempFile.getAbsolutePath());
            }
        }
    }


    private String getTokenByName(String name) {
        // 1. 获取二代Token
        String token = redisCache.getCacheObject(VIVO_TOKEN_CACHE_PREFIX+name);
        if (token == null || token.isEmpty()) {
            // 本地缓存未命中，查询数据库
            token = vivoTokenService.selectVivoTokenByName(name);
            redisCache.setCacheObject(VIVO_TOKEN_CACHE_PREFIX+name, token, 2, TimeUnit.DAYS);
        }
        return token;
    }

}